package org.wu.framework.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 反射工具
 */
public class ReflexUtils {

    /**
     * 获取目标class中的字段
     *
     * @param targetClass 目标class
     * @param fieldName   字段名称
     * @return 字段
     */
    public static Field findDeclaredField(Class<?> targetClass, String fieldName) {
        try {
            return targetClass.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取目标对象指定字段对象
     *
     * @param targetObject 目标对象
     * @param fieldName    字段名称
     * @return 字段
     */
    public static <T> T findDeclaredFieldBean(Object targetObject, String fieldName) {
        if (targetObject == null) {
            return null;
        }
        Class<?> targetClass = targetObject.getClass();
        Field declaredField = findDeclaredField(targetClass, fieldName);
        return findDeclaredFieldBean(targetObject, declaredField);
    }

    /**
     * 获取目标对象指定字段对象
     *
     * @param targetObject  目标对象
     * @param declaredField 字段
     * @return 字段
     */
    public static <T> T findDeclaredFieldBean(Object targetObject, Field declaredField) {
        if (targetObject == null) {
            return null;
        }

        try {
            if (!declaredField.canAccess(targetObject)) {
                declaredField.setAccessible(true);
            }
            return (T) declaredField.get(targetObject);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取目标class中的字段
     *
     * @param targetClass 目标class
     * @param fieldName   字段名称
     * @return 字段
     */
    public static Field findField(Class<?> targetClass, String fieldName) {
        try {
            return targetClass.getField(fieldName);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取目标class中的方法
     *
     * @param targetClass    目标class
     * @param method         方法名
     * @param parameterTypes 参数类型
     * @return 方法
     */
    public static Method findDeclaredMethod(Class<?> targetClass, String method, Class<?>... parameterTypes) {
        try {
            return targetClass.getDeclaredMethod(method, parameterTypes);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 判断目标class中是否含有此方法
     *
     * @param targetClass    目标class
     * @param method         方法名
     * @param parameterTypes 参数类型
     * @return 方法
     */
    public static Boolean existsDeclaredMethod(Class<?> targetClass, String method, Class<?>... parameterTypes) {
        try {
            targetClass.getDeclaredMethod(method, parameterTypes);
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    /**
     * 获取目标class中的方法
     *
     * @param targetClass    目标class
     * @param method         方法名
     * @param parameterTypes 参数类型
     * @return 方法
     */
    public static Method findMethod(Class<?> targetClass, String method, Class<?>... parameterTypes) {
        try {
            return targetClass.getMethod(method, parameterTypes);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 反射获取字段数据
     *
     * @param bean      目标 对象
     * @param filedName 字段名称
     * @return 方法
     */
    public static Object invokeDeclaredField(Object bean, String filedName) {
        if (bean == null) {
            return null;
        }
        if (bean instanceof Map<?, ?>) {
            return ((Map<?, ?>) bean).get(filedName);
        }
        Field declaredField = findDeclaredField(bean.getClass(), filedName);
        return invokeDeclaredField(bean, declaredField);
    }

    /**
     * 反射获取字段数据
     *
     * @param bean          目标 对象
     * @param declaredField 字段
     * @return 方法
     */
    public static Object invokeDeclaredField(Object bean, Field declaredField) {
        if (bean == null) {
            return null;
        }
        Object invoke = null;
        try {
            declaredField.setAccessible(true);
            invoke = declaredField.get(bean);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return invoke;
    }

    /**
     * 反射获取字段数据
     *
     * @param bean      目标 对象
     * @param filedName 字段名称
     * @return 方法
     */
    public static Map<?, ?> invokeDeclaredFieldWithMap(Object bean, String filedName) {
        Object invokeDeclaredField = invokeDeclaredField(bean, filedName);
        if (invokeDeclaredField == null) {
            return null;
        }
        if (invokeDeclaredField instanceof Map<?, ?>) {
            return (Map<?, ?>) invokeDeclaredField;
        } else {
            throw new RuntimeException("获取对象不是map对象:" + invokeDeclaredField.getClass().getName());
        }
    }

    /**
     * 反射获取字段数据
     *
     * @param bean      目标 对象 user:{role:{id:1,name:名称},id:1,age:19}
     * @param filedName 字段名称 role.id
     * @return 方法
     */
    public static Object invokeDeclaredFieldWithDeep(Object bean, String filedName) {
        Object fieldValue = bean;
        for (String propName : filedName.split("\\.")) {
            Object invokeDeclaredField = invokeDeclaredField(fieldValue, propName);
            if (invokeDeclaredField == null) {
                fieldValue = null;
            } else {
                fieldValue = invokeDeclaredField;
            }
        }
        return fieldValue;
    }

    /**
     * 执行方法
     *
     * @param bean           目标 对象
     * @param declaredMethod 方法
     * @param parameter      参数 参数不能为空否则无法获取参数类型
     * @return 方法
     */
    public static Object invokeDeclaredMethod(Object bean, Method declaredMethod, Object... parameter) {
        if (bean == null) {
            return null;
        }
        Object invoke = null;
        try {
            invoke = declaredMethod.invoke(bean, parameter);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return invoke;

    }

    /**
     * 执行方法
     *
     * @param bean      目标 对象
     * @param method    方法名
     * @param parameter 参数 参数不能为空否则无法获取参数类型
     * @return 方法
     */
    public static Object invokeDeclaredMethod(Object bean, String method, Object... parameter) {
        if (bean == null) {
            return null;
        }
        Class<?> targetClass = bean.getClass();
        Method declaredMethod = findDeclaredMethod(targetClass, method);
        return invokeDeclaredMethod(bean, declaredMethod, parameter);

    }

    /**
     * 反射 获取对象
     *
     * @param t   对象class
     * @param <T> class 范型
     * @return T
     */
    public static <T> T newInstance(Class<T> t) {
        if (t == null) {
            throw new IllegalArgumentException("class is null ");
        }
        try {
            return t.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException |
                 IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }


    private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
        List<Method> result = null;
        for (Class<?> ifc : clazz.getInterfaces()) {
            for (Method ifcMethod : ifc.getMethods()) {
                if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
                    if (result == null) {
                        result = new ArrayList<>();
                    }
                    result.add(ifcMethod);
                }
            }
        }
        return result;
    }

    /**
     * Determine whether the given method is an "equals" method.
     *
     * @see java.lang.Object#equals(Object)
     */
    public static boolean isEqualsMethod(Method method) {
        if (method == null) {
            return false;
        }
        if (method.getParameterCount() != 1) {
            return false;
        }
        if (!method.getName().equals("equals")) {
            return false;
        }
        return method.getParameterTypes()[0] == Object.class;
    }

    /**
     * Determine whether the given method is a "hashCode" method.
     *
     * @see java.lang.Object#hashCode()
     */
    public static boolean isHashCodeMethod(Method method) {
        return method != null && method.getParameterCount() == 0 && method.getName().equals("hashCode");
    }

    /**
     * Determine whether the given method is a "toString" method.
     *
     * @see java.lang.Object#toString()
     */
    public static boolean isToStringMethod(Method method) {
        return (method != null && method.getParameterCount() == 0 && method.getName().equals("toString"));
    }

    /**
     * Determine whether the given method is originally declared by {@link java.lang.Object}.
     */
    public static boolean isObjectMethod(Method method) {
        return (method != null && (method.getDeclaringClass() == Object.class ||
                isEqualsMethod(method) || isHashCodeMethod(method) || isToStringMethod(method)));
    }

}
